package model

import (
	"fmt"
	"github.com/jinzhu/gorm"
)

type (
	RoleMenuRelations []*RoleMenu

	RoleMenu struct {
		Model
		RoleID   uint `gorm:"column:role_id;index;not null;"`
		MenuID   uint `gorm:"column:menu_id;index;not null;"`
		ActionID uint `gorm:"column:action_id;index;not null;"`
	}
)

func AddRoleMenu(roleMenu *RoleMenu, tx *gorm.DB) error {
	if err := tx.Create(roleMenu).Error; err != nil {
		return err
	}
	return nil
}

func DeleteRoleMenuByRoleID(roleID int, tx *gorm.DB) error {
	if err := tx.Delete(&RoleMenu{}, "role_id = ?", roleID).Error; err != nil {
		return err
	}
	return nil
}

func GetRoleMenuRelationsWithRoleID(roleID uint) (RoleMenuRelations, error) {
	relations := make(RoleMenuRelations, 0)
	if err := db.Model(&RoleMenu{}).Where("role_id = ?", roleID).Find(&relations).Error; err != nil {
		return nil, err
	}
	return relations, nil
}

func GetAllRoleMenuRelations() (RoleMenuRelations, error) {
	relations := make(RoleMenuRelations, 0)
	if err := db.Model(&RoleMenu{}).Find(&relations).Error; err != nil {
		return nil, err
	}
	return relations, nil
}

func (rel RoleMenuRelations) ToMenuIDs() []uint {
	ids := make([]uint, len(rel))
	uniqueM := make(map[uint]struct{})
	for idx, rel := range rel {
		if _, ok := uniqueM[rel.MenuID]; ok {
			continue
		}
		ids[idx] = rel.MenuID
		uniqueM[rel.MenuID] = struct{}{}
	}
	return ids
}

func (rel RoleMenuRelations) ToActionIDs() []uint {
	ids := make([]uint, len(rel))
	uniqueM := make(map[uint]struct{})
	for idx, rel := range rel {
		if _, ok := uniqueM[rel.ActionID]; ok {
			continue
		}
		ids[idx] = rel.ActionID
		uniqueM[rel.ActionID] = struct{}{}
	}
	return ids
}

func (rel RoleMenuRelations) ToMap() map[string]*RoleMenu {
	m := make(map[string]*RoleMenu)
	for _, r := range rel {
		m[fmt.Sprintf("%d-%d", r.MenuID, r.ActionID)] = r
	}
	return m
}

func (rel RoleMenuRelations) ToMapRoleIDAsKey() map[uint]RoleMenuRelations {
	relMap := make(map[uint]RoleMenuRelations)
	for _, rel := range rel {
		relMap[rel.RoleID] = append(relMap[rel.RoleID], rel)
	}
	return relMap
}

func UpdateRoleMenus(roleID int, menus *Menus) error {

	tx := db.Begin()

	for _, menu := range *menus {
		if len(menu.Actions) == 0 {
			roleMenu := new(RoleMenu)
			roleMenu.RoleID = uint(roleID)
			roleMenu.MenuID = menu.ID
			roleMenu.ActionID = 0
			if err := AddRoleMenu(roleMenu, tx); err != nil {
				tx.Rollback()
				return err
			}
		} else {
			for _, action := range menu.Actions {
				roleMenu := new(RoleMenu)
				roleMenu.RoleID = uint(roleID)
				roleMenu.MenuID = menu.ID
				roleMenu.ActionID = action.ID
				if err := AddRoleMenu(roleMenu, tx); err != nil {
					tx.Rollback()
					return err
				}
			}
		}
	}

	tx.Commit()
	return nil
}
